package com.yekaile.carsystem.controller;


import cn.hutool.core.date.DateUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yekaile.carsystem.common.Constant;
import com.yekaile.carsystem.controller.vo.AuthorityVO;
import com.yekaile.carsystem.controller.vo.LoginVO;
import com.yekaile.carsystem.controller.vo.MenuVO;
import com.yekaile.carsystem.controller.vo.UserVO;
import com.yekaile.carsystem.entity.Role;
import com.yekaile.carsystem.entity.SystemLog;
import com.yekaile.carsystem.entity.User;
import com.yekaile.carsystem.exception.CustomException;
import com.yekaile.carsystem.service.SystemLogService;
import com.yekaile.carsystem.service.UserService;
import com.yekaile.carsystem.util.*;
import lombok.AllArgsConstructor;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;

/**
 * @author yekaile
 */
@RestController
@RequestMapping("user")
@PropertySource("classpath:config.properties")
public class UserController {


    /**
     * RefreshToken过期时间
     */
    @Value("${refreshTokenExpireTime}")
    private String refreshTokenExpireTime;

    private final UserService userService;

    HttpServletRequest request;

    RedisUtil redisUtil;

    SystemLogService systemLogService;

    @Autowired
    public UserController(UserService userService, HttpServletRequest request, RedisUtil redisUtil, SystemLogService systemLogService) {
        this.userService = userService;
        this.request = request;
        this.redisUtil = redisUtil;
        this.systemLogService = systemLogService;
    }

    private boolean userEquals(User userDb, User user) {
        String password = SecurityUtil.md5(user.getPassword(), userDb.getSalt());
        return userDb.getUsername().equals(user.getUsername()) && userDb.getPassword().equals(password);
    }

    @PostMapping("login")
    public Result<String> login(@RequestBody User user, HttpServletResponse response) {
        User userDb = userService.getUser(user.getUsername());
        if (this.userEquals(userDb, user)) {
            if (redisUtil.hasKey(Constant.PREFIX_SHIRO_CACHE + user.getUsername())) {
                redisUtil.del(Constant.PREFIX_SHIRO_CACHE + user.getUsername());
            }
            String currentTimeMillis = String.valueOf(System.currentTimeMillis());
            String today = DateUtil.today();
            if (redisUtil.hasKey(Constant.VISIT)) {
                if (redisUtil.hHasKey(Constant.VISIT, today)) {
                    int num = (int) redisUtil.hget(Constant.VISIT, today);
                    redisUtil.hset(Constant.VISIT, today, num + 1);
                } else {
                    redisUtil.hset(Constant.VISIT, today, 1);
                }
            } else {
                redisUtil.hset(Constant.VISIT, today, 1);
            }

            String time = DateUtil.now();
            user = userService.getUser(user.getUsername());
            Map<String, Object> map = new LinkedHashMap<>();
            map.put(Constant.USERID, user.getUserId());
            map.put(Constant.USERNAME, user.getUsername());
            map.put(Constant.LOGIN_TIME, time);
            String ip = IPUtils.getIP(request);
            map.put(Constant.IP, ip);
            map.put(Constant.LOCATION, IPUtils.getIpInfo(ip));
            map.put(Constant.LOGIN_BROWSER, " ");
            map.put(Constant.LOGIN_OS, " ");
            redisUtil.hmset(Constant.LOGIN_LOG + time, map);
            redisUtil.set(Constant.PREFIX_SHIRO_REFRESH_TOKEN + user.getUsername(), currentTimeMillis, Long.parseLong(refreshTokenExpireTime));
            String token = JwtUtil.sign(user.getUsername(), currentTimeMillis);
            response.setHeader("Authorization", token);
            response.setHeader("Access-Control-Expose-Headers", "Authorization");
            return new Result<>(HttpStatus.SC_OK, "success", "success");
        }
        throw new AuthenticationException("密码错误");
    }


    @GetMapping("getUserInfo")
    @RequiresPermissions(value = {"authority:distribution", "user:select"}, logical = Logical.OR)
    public Result<UserVO> listRoleByUser(Integer userId) {
        User user = userService.getUser(userId);
        List<Role> roles = userService.getRoles(userId);
        UserVO userVO = new UserVO(user, roles);
        return new Result<>(HttpStatus.SC_OK, "success", userVO);
    }

    @PutMapping("updateUser")
    @RequiresPermissions(value = {"authority:distribution", "user:update"}, logical = Logical.AND)
    public Result<String> updateUser(@RequestBody UserVO userVO) {
        User user = userVO.getUser();
        user.setSalt(user.getUsername());
        user.setPassword(SecurityUtil.md5(user.getPassword(), user.getSalt()));
        userVO.setUser(user);
        userService.updateUser(userVO);
        return new Result<>(HttpStatus.SC_CREATED, "success", "success");
    }

    @GetMapping("getPermission")
    @RequiresAuthentication
    public Result<AuthorityVO> getPermission() {
        String username = JwtUtil.getClaim(request.getHeader(Constant.AUTHORIZATION_HEADER), Constant.USERNAME);
        User user = userService.getUser(username);
        user.setSalt(null);
        Integer userId = user.getUserId();
        List<Role> roleList = userService.getRoles(userId);
        Set<MenuVO> menuVOS = userService.getMenuVOS(userId);
        AuthorityVO authorityVO = new AuthorityVO(menuVOS, roleList, user);
        return new Result<>(HttpStatus.SC_OK, "success", authorityVO);
    }


    @GetMapping("getAllUserPermission")
    @RequiresPermissions("user:select")
    public Result<PageInfo<AuthorityVO>> listAllUserPermission(@RequestParam(defaultValue = "1", required = false) Integer pageNum,
                                                               @RequestParam(defaultValue = "4", required = false) Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userService.getAllUser();
        List<AuthorityVO> authorityVOS = new ArrayList<>();
        users.forEach(user -> {
            List<Role> roleList = userService.getRoles(user.getUserId());
            Set<MenuVO> menuVOS = userService.getMenuVOS(user.getUserId());
            AuthorityVO authorityVO = new AuthorityVO(menuVOS, roleList, user);
            authorityVOS.add(authorityVO);
        });
        PageInfo<AuthorityVO> pageInfo = new PageInfo<>(authorityVOS);
        return new Result<>(HttpStatus.SC_OK, "success", pageInfo);
    }


    @DeleteMapping("deleteUser/{id}")
    @RequiresPermissions("user:delete")
    public Result<String> deleteUser(@PathVariable("id") Integer userId) {
        userService.deleteUser(userId);
        return new Result<>(HttpStatus.SC_NO_CONTENT, "success", "success");
    }


    @PostMapping("addUser")
    public Result<String> addUser(@RequestBody @Valid UserVO userVO) {

        User user = userVO.getUser();
        String salt = UUID.randomUUID().toString();
        String password = SecurityUtil.md5(user.getPassword(), salt);
        user.setPassword(password);
        user.setSalt(salt);
        userService.addUser(userVO);
        return new Result<>(HttpStatus.SC_CREATED, "success", "success");
    }


    @GetMapping("online")
    @RequiresRoles("admin")
    public Result<Set<LoginVO>> online() {
        Set<String> keys = redisUtil.keys(Constant.PREFIX_SHIRO_REFRESH_TOKEN + "*");
        Set<LoginVO> loginVOS = new HashSet<>();
        for (String key : keys) {
            String[] arr = key.split(":");
            String username = arr[arr.length - 1];
            Date loginTime = new Date((Long.parseLong((String) redisUtil.get(key))));
            LoginVO loginVO = new LoginVO(userService.getUser(username), loginTime);
            loginVOS.add(loginVO);
        }
        return new Result<>(HttpStatus.SC_OK, "success", loginVOS);
    }


    @DeleteMapping("deleteOnline/{id}")
    @RequiresRoles("admin")
    public Result<String> deleteOnline(@PathVariable("id") Integer userId) {
        User user = userService.getUser(userId);
        String key = Constant.PREFIX_SHIRO_REFRESH_TOKEN + user.getUsername();
        if (redisUtil.hasKey(key)) {
            redisUtil.del(key);
            return new Result<>(HttpStatus.SC_NO_CONTENT, "删除成功", "success");
        } else {
            throw new CustomException("删除失败，refreshtoken不存在");
        }
    }

    @GetMapping("getVisitor")
    @RequiresRoles("admin")
    public Result<Map<Object, Object>> listVisitor() {
        Map<Object, Object> map = redisUtil.hmget(Constant.VISIT);
        return new Result<>(HttpStatus.SC_OK, "success", map);
    }

    @GetMapping("getLoginLogs")
    @RequiresRoles("admin")
    public Result<List<Map<Object, Object>>> listLoginLog() {
        List<Map<Object, Object>> loginLogs = new ArrayList<>();
        Set<String> keys = redisUtil.keys(Constant.LOGIN_LOG + "*");
        for (String key : keys) {
            Map<Object, Object> map = redisUtil.hmget(key);
            loginLogs.add(map);
        }
        return new Result<>(HttpStatus.SC_OK, "success", loginLogs);
    }

    @GetMapping("getSystemLogs")
    @RequiresRoles("admin")
    public Result<PageInfo<SystemLog>> listSystemLog(@RequestParam(defaultValue = "1", required = false) Integer pageNum,
                                                     @RequestParam(defaultValue = "8", required = false) Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<SystemLog> systemLogs = systemLogService.listAllSystemLog();
        PageInfo<SystemLog> pageInfo = new PageInfo<>(systemLogs);
        return new Result<>(HttpStatus.SC_OK, "success", pageInfo);
    }

    @DeleteMapping("deleteSystemLogs/{id}")
    @RequiresRoles("admin")
    public Result<String> deleteOPLogs(@PathVariable("id") Integer logId) {
        systemLogService.deleteSystemLog(logId);
        return new Result<>(HttpStatus.SC_NO_CONTENT, "success", "success");
    }

    @DeleteMapping("deleteLoginLogs")
    @RequiresRoles("admin")
    public Result<String> deleteLoginLogs(String key) {
        redisUtil.del(Constant.LOGIN_LOG + key);
        return new Result<>(HttpStatus.SC_NO_CONTENT, "success", "success");
    }


}
